package com.doubao.file.service.impl;

import com.doubao.file.config.MinioConfig;
import com.doubao.file.dto.FileInfoResponse;
import com.doubao.file.security.UserContext;
import com.doubao.file.service.FileService;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 文件服务实现类 - 优化版
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class FileServiceImpl implements FileService {

    private final MinioClient minioClient;
    private final MinioConfig minioConfig;

    @Value("${file.avatar-prefix}")
    private String avatarPrefix;

    @Value("${file.chat-prefix}")
    private String chatPrefix;

    @Value("${file.allowed-image-types}")
    private String allowedImageTypes;

    @Value("${file.max-size:10MB}")
    private String maxFileSize;

    /**
     * 上传用户头像
     */
    @Override
    public FileInfoResponse uploadAvatar(MultipartFile file, Long userId) throws Exception {
        // 检查文件类型
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        if (!isAllowedImageType(extension)) {
            throw new IllegalArgumentException("不支持的图片类型: " + extension + "，允许的类型: " + allowedImageTypes);
        }

        // 检查文件大小
        validateFileSize(file);

        // 生成对象名
        String objectName = generateAvatarObjectName(userId, extension);

        // 上传到头像桶
        PutObjectArgs args = PutObjectArgs.builder()
                .bucket(minioConfig.getAvatarBucket())
                .object(objectName)
                .stream(file.getInputStream(), file.getSize(), -1)
                .contentType(file.getContentType())
                // 添加元数据
                .userMetadata(Map.of(
                        "userId", userId.toString(),
                        "originalFilename", file.getOriginalFilename(),
                        "uploadTime", LocalDateTime.now().toString()
                ))
                .build();

        minioClient.putObject(args);

        // 返回文件信息
        FileInfoResponse response = new FileInfoResponse();
        response.setFileName(file.getOriginalFilename());
        response.setObjectName(objectName);
        response.setSize(file.getSize());
        response.setContentType(file.getContentType());
        response.setUrl(getAvatarUrl(objectName));
        response.setUploadTime(LocalDateTime.now());

        log.info("用户头像上传成功: userId={}, objectName={}", userId, objectName);
        return response;
    }

    /**
     * 上传聊天记录文件
     */
    @Override
    public FileInfoResponse uploadChatRecord(MultipartFile file, Long userId, String chatId) throws Exception {
        // 检查文件大小
        validateFileSize(file);

        // 生成对象名
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        String objectName = generateChatObjectName(userId, chatId, extension);

        // 上传到聊天记录桶
        PutObjectArgs args = PutObjectArgs.builder()
                .bucket(minioConfig.getChatBucket())
                .object(objectName)
                .stream(file.getInputStream(), file.getSize(), -1)
                .contentType(file.getContentType())
                // 添加元数据
                .userMetadata(Map.of(
                        "userId", userId.toString(),
                        "chatId", chatId,
                        "originalFilename", file.getOriginalFilename(),
                        "uploadTime", LocalDateTime.now().toString()
                ))
                .build();

        minioClient.putObject(args);

        // 返回文件信息
        FileInfoResponse response = new FileInfoResponse();
        response.setFileName(file.getOriginalFilename());
        response.setObjectName(objectName);
        response.setSize(file.getSize());
        response.setContentType(file.getContentType());
        response.setUrl(getChatRecordUrl(objectName));
        response.setUploadTime(LocalDateTime.now());

        log.info("聊天记录文件上传成功: userId={}, chatId={}, objectName={}", userId, chatId, objectName);
        return response;
    }

    /**
     * 获取文件访问URL
     */
    @Override
    @Cacheable(value = "fileUrls", key = "'file:' + #objectName", unless = "#result == null")
    public String getFileUrl(String objectName) throws Exception {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .bucket(minioConfig.getBucketName())
                        .object(objectName)
                        .method(Method.GET)
                        .expiry(7, TimeUnit.DAYS) // 7天有效期
                        .build());
    }

    /**
     * 获取头像访问URL
     */
    @Override
    @Cacheable(value = "avatarUrls", key = "'avatar:' + #objectName", unless = "#result == null")
    public String getAvatarUrl(String objectName) throws Exception {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .bucket(minioConfig.getAvatarBucket())
                        .object(objectName)
                        .method(Method.GET)
                        .expiry(7, TimeUnit.DAYS) // 7天有效期
                        .build());
    }

    /**
     * 获取聊天记录访问URL
     */
    @Override
    @Cacheable(value = "chatUrls", key = "'chat:' + #objectName", unless = "#result == null")
    public String getChatRecordUrl(String objectName) throws Exception {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .bucket(minioConfig.getChatBucket())
                        .object(objectName)
                        .method(Method.GET)
                        .expiry(7, TimeUnit.DAYS) // 7天有效期
                        .build());
    }

    /**
     * 删除文件
     */
    @Override
    public void deleteFile(String objectName) throws Exception {
        // 根据对象名判断桶
        String bucketName;
        if (objectName.startsWith(avatarPrefix)) {
            bucketName = minioConfig.getAvatarBucket();
        } else if (objectName.startsWith(chatPrefix)) {
            bucketName = minioConfig.getChatBucket();
        } else {
            bucketName = minioConfig.getBucketName();
        }

        minioClient.removeObject(
                RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build());

        log.info("文件删除成功: objectName={}", objectName);
    }

    /**
     * 批量删除文件
     */
    @Override
    public void deleteFiles(List<String> objectNames) throws Exception {
        for (String objectName : objectNames) {
            try {
                deleteFile(objectName);
            } catch (Exception e) {
                log.error("删除文件失败: {}, 继续处理下一个文件", objectName, e);
            }
        }
    }

    /**
     * 获取聊天文件列表
     */
    @Override
    public List<FileInfoResponse> getChatFiles(Long userId, String chatId) throws Exception {
        // 构建前缀
        String prefix = chatPrefix + "/" + userId + "/" + chatId + "/";

        // 列出文件
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(minioConfig.getChatBucket())
                        .prefix(prefix)
                        .recursive(true)
                        .build());

        List<FileInfoResponse> fileList = new ArrayList<>();
        for (Result<Item> result : results) {
            Item item = result.get();

            FileInfoResponse file = new FileInfoResponse();
            file.setObjectName(item.objectName());
            file.setFileName(getFileNameFromObjectName(item.objectName()));
            file.setSize(item.size());
            file.setContentType(getContentTypeByExtension(FilenameUtils.getExtension(item.objectName())));
            file.setUrl(getChatRecordUrl(item.objectName()));
            file.setUploadTime(item.lastModified().toLocalDateTime());

            fileList.add(file);
        }

        return fileList;
    }

    /**
     * 获取用户头像列表
     */
    @Override
    public List<FileInfoResponse> getUserFiles(Long userId) throws Exception {
        // 构建前缀
        String prefix = avatarPrefix + "/" + userId + "/";

        // 列出文件
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(minioConfig.getAvatarBucket())
                        .prefix(prefix)
                        .recursive(true)
                        .build());

        List<FileInfoResponse> fileList = new ArrayList<>();
        for (Result<Item> result : results) {
            Item item = result.get();

            FileInfoResponse file = new FileInfoResponse();
            file.setObjectName(item.objectName());
            file.setFileName(getFileNameFromObjectName(item.objectName()));
            file.setSize(item.size());
            file.setContentType(getContentTypeByExtension(FilenameUtils.getExtension(item.objectName())));
            file.setUrl(getAvatarUrl(item.objectName()));
            file.setUploadTime(item.lastModified().toLocalDateTime());

            fileList.add(file);
        }

        return fileList;
    }

    /**
     * 保存聊天消息为文本文件
     */
    @Override
    public FileInfoResponse saveChatMessage(Long userId, String chatId, String content, String messageId) throws Exception {
        // 验证参数
        if (userId == null || !StringUtils.hasText(chatId) || !StringUtils.hasText(content)) {
            throw new IllegalArgumentException("用户ID、聊天ID和消息内容不能为空");
        }

        if (!StringUtils.hasText(messageId)) {
            messageId = UUID.randomUUID().toString();
        }

        // 生成文件名
        String fileName = "message_" + messageId + ".txt";
        String objectName = chatPrefix + "/" + userId + "/" + chatId + "/" + fileName;

        // 将内容转换为字节流
        byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
        ByteArrayInputStream inputStream = new ByteArrayInputStream(contentBytes);

        // 上传到聊天记录桶
        PutObjectArgs args = PutObjectArgs.builder()
                .bucket(minioConfig.getChatBucket())
                .object(objectName)
                .stream(inputStream, contentBytes.length, -1)
                .contentType("text/plain")
                // 添加元数据
                .userMetadata(Map.of(
                        "userId", userId.toString(),
                        "chatId", chatId,
                        "messageId", messageId,
                        "uploadTime", LocalDateTime.now().toString()
                ))
                .build();

        minioClient.putObject(args);

        // 返回文件信息
        FileInfoResponse response = new FileInfoResponse();
        response.setFileName(fileName);
        response.setObjectName(objectName);
        response.setSize((long) contentBytes.length);
        response.setContentType("text/plain");
        response.setUrl(getChatRecordUrl(objectName));
        response.setUploadTime(LocalDateTime.now());

        log.info("聊天消息保存成功: userId={}, chatId={}, messageId={}, objectName={}",
                userId, chatId, messageId, objectName);
        return response;
    }

    /**
     * 生成头像对象名称
     */
    private String generateAvatarObjectName(Long userId, String extension) {
        LocalDateTime now = LocalDateTime.now();
        String timestamp = now.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        return avatarPrefix + "/" + userId + "/" + timestamp + "_" + UUID.randomUUID().toString() + "." + extension;
    }

    /**
     * 生成聊天记录对象名称
     */
    private String generateChatObjectName(Long userId, String chatId, String extension) {
        LocalDateTime now = LocalDateTime.now();
        String timestamp = now.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        return chatPrefix + "/" + userId + "/" + chatId + "/" + timestamp + "_" + UUID.randomUUID().toString() + "." + extension;
    }

    /**
     * 检查是否为允许的图片类型
     */
    private boolean isAllowedImageType(String extension) {
        if (extension == null) {
            return false;
        }

        String[] allowed = allowedImageTypes.split(",");
        for (String type : allowed) {
            if (extension.equalsIgnoreCase(type.trim())) {
                return true;
            }
        }

        return false;
    }

    /**
     * 检查文件大小
     */
    private void validateFileSize(MultipartFile file) {
        long maxSize = parseSize(maxFileSize);
        if (file.getSize() > maxSize) {
            throw new IllegalArgumentException("文件大小超过限制: " + maxFileSize);
        }
    }

    /**
     * 解析文件大小配置
     */
    private long parseSize(String size) {
        size = size.toUpperCase();
        if (size.endsWith("KB")) {
            return Long.parseLong(size.substring(0, size.length() - 2)) * 1024;
        } else if (size.endsWith("MB")) {
            return Long.parseLong(size.substring(0, size.length() - 2)) * 1024 * 1024;
        } else if (size.endsWith("GB")) {
            return Long.parseLong(size.substring(0, size.length() - 2)) * 1024 * 1024 * 1024;
        }
        return Long.parseLong(size);
    }

    /**
     * 从对象名获取文件名
     */
    private String getFileNameFromObjectName(String objectName) {
        int lastSlashIndex = objectName.lastIndexOf('/');
        if (lastSlashIndex >= 0 && lastSlashIndex < objectName.length() - 1) {
            return objectName.substring(lastSlashIndex + 1);
        }
        return objectName;
    }

    /**
     * 根据扩展名获取内容类型
     */
    private String getContentTypeByExtension(String extension) {
        if (extension == null) {
            return "application/octet-stream";
        }

        switch (extension.toLowerCase()) {
            case "jpg":
            case "jpeg":
                return "image/jpeg";
            case "png":
                return "image/png";
            case "gif":
                return "image/gif";
            case "txt":
                return "text/plain";
            case "html":
                return "text/html";
            case "pdf":
                return "application/pdf";
            case "doc":
                return "application/msword";
            case "docx":
                return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
            default:
                return "application/octet-stream";
        }
    }
}